#include "ImageDecoderEx.h"

struct CImageDecoderEx::CRemote : public CActive
	{
	CImageDecoder* iImageDecoder;
	TRequestStatus* iConvertStatus;
	TThreadId iThreadId;
	CFbsBitmap* iDestination;
	TBool iCancelled;
	CRemote() : CActive(EPriorityStandard), iDestination(NULL), iImageDecoder(NULL), iCancelled(EFalse)
		{
		}
	void Init()
		{
		CActiveScheduler::Add(this);
		}
	void Convert(TRequestStatus* aConvertStatus, TInt aDestinationHandle, TThreadId aThreadId)
		{
			iConvertStatus = aConvertStatus;
			iThreadId = aThreadId;
			delete iDestination;
			iDestination = new (ELeave) CFbsBitmap;
			User::LeaveIfError( iDestination->Duplicate( aDestinationHandle ) );
			iImageDecoder->Convert(&iStatus, *iDestination);
			SetActive();
		}
	void RunL()
		{
		if(!iCancelled)
			{
			RThread thread;
			thread.Open(iThreadId);
			thread.RequestComplete(iConvertStatus, iStatus.Int());
			thread.Close();
			}
		}
	void DoCancel()
		{
		iImageDecoder->Cancel();
		RThread thread;
		thread.Open(iThreadId);
		thread.RequestComplete(iConvertStatus, KErrCancel);
		thread.Close();
		}
	~CRemote()
		{
			delete iImageDecoder;
			delete iDestination;
		}
	};



void CImageDecoderEx::StartThread()
	{
	User::LeaveIfError( sWorkerThread.Create(KNullDesC, &ThreadFunction, KDefaultStackSize, NULL, NULL) );
	sWorkerThread.SetPriority(EPriorityLess);
	TRequestStatus workerThreadInit;
	sWorkerThread.Rendezvous(workerThreadInit);
	sWorkerThread.Resume();
	User::WaitForRequest(workerThreadInit);
	}
void CImageDecoderEx::StopThread()
	{
	TRequestStatus logonReq;
	sWorkerThread.Logon(logonReq);
	TThreadMessage message = { TThreadMessage::EXIT_THREAD };
	sMsgQueue.SendBlocking(message);	
	User::WaitForRequest(logonReq);
	sWorkerThread.Close();
	}

struct CImageDecoderEx::CThreadCallback : public CActive
	{
	RMsgQueue<TThreadMessage>& iMsgQueue;
	CThreadCallback(RMsgQueue<TThreadMessage>& aMsgQueue) : iMsgQueue(aMsgQueue), CActive(CActive::EPriorityStandard)
		{
		CActiveScheduler::Add(this);	
		}
	void Listen()
		{
		iMsgQueue.NotifyDataAvailable(iStatus);
		SetActive();
		}
	void RunL()
		{
		TThreadMessage message;
		sMsgQueue.Receive(message);
		switch(message.iOperation)
			{
			case TThreadMessage::INIT:
				message.iRemote->Init();
				break;
			case TThreadMessage::DATANEWL:
				message.iRemote->iImageDecoder = CImageDecoder::DataNewL(sFs, *message.iSourceData);
				break;
			case TThreadMessage::CONVERT:
				message.iRemote->Convert(message.iConvertStatus, message.iDestinationHandle, message.iThreadId);
				break;
			case TThreadMessage::CANCEL:
				message.iRemote->Cancel();
				break;
			case TThreadMessage::DELETE:
				delete message.iRemote;
				break;
			case TThreadMessage::EXIT_THREAD:
				CActiveScheduler::Stop();
				break;
			}
		if(message.iOperation != TThreadMessage::EXIT_THREAD)
			Listen();
		}
	void DoCancel()
		{
		
		}
	};

TInt CImageDecoderEx::ThreadFunction(TAny* aPtr)
	{
	RDebug::Print(_L("Thread started!!"));
	CActiveScheduler* scheduler = new (ELeave) CActiveScheduler;
	CActiveScheduler::Install(scheduler);
	CTrapCleanup* cleanup = CTrapCleanup::New();

	User::LeaveIfError( sFs.Connect() );
	
	User::LeaveIfError( RFbsSession::Connect() );
	
	User::LeaveIfError( sMsgQueue.CreateLocal(16) );
	
	CThreadCallback* callback = new(ELeave) CThreadCallback(sMsgQueue);
	callback->Listen();
	
	RThread::Rendezvous(KErrNone); // notify initialization
	
	CActiveScheduler::Start();
	
	
	RFbsSession::Disconnect();
	
	sFs.Close();
	
	delete callback;
	
	sMsgQueue.Close();
	
	delete cleanup;
	delete scheduler;
	
	REComSession::FinalClose(); // If not, there is a memory leak... http://wiki.forum.nokia.com/index.php/How_to_decode_images_synchronously
	
	RDebug::Print(_L("Thread stopping!!"));	
	}
RThread CImageDecoderEx::sWorkerThread;
RFs CImageDecoderEx::sFs;
RMsgQueue<CImageDecoderEx::TThreadMessage> CImageDecoderEx::sMsgQueue;

void CImageDecoderEx::Convert(TRequestStatus* aRequestStatus, CFbsBitmap& aDestination)
	{
	TThreadMessage message = { TThreadMessage::CONVERT, iRemote };
	message.iConvertStatus = aRequestStatus;
	message.iDestinationHandle = aDestination.Handle();
	RThread thread;
	message.iThreadId = thread.Id();		
	thread.Close();
	*aRequestStatus = KRequestPending;
	sMsgQueue.SendBlocking(message);
	}

void CImageDecoderEx::Cancel()
	{
	iRemote->iCancelled = ETrue;
	TThreadMessage message = { TThreadMessage::CANCEL, iRemote };
	sMsgQueue.SendBlocking(message);	
	}

CImageDecoderEx::CImageDecoderEx() : iImageDecoder(NULL)
	{
	}

CImageDecoderEx::~CImageDecoderEx()
	{
	delete iImageDecoder;
	TThreadMessage message = { TThreadMessage::DELETE, iRemote };
	sMsgQueue.SendBlocking(message);
	}

CImageDecoderEx* CImageDecoderEx::DataNewL(RFs& aFs, const TDesC8& aSourceData, const CImageDecoder::TOptions aOptions)
	{
	CImageDecoderEx* ret = new (ELeave) CImageDecoderEx;
	ret->iImageDecoder = CImageDecoder::DataNewL(aFs, aSourceData, aOptions);
	ret->iRemote = new (ELeave) CRemote;
	{
	TThreadMessage message = { TThreadMessage::INIT, ret->iRemote };
	sMsgQueue.SendBlocking(message);
	}
	{
	TThreadMessage message = { TThreadMessage::DATANEWL, ret->iRemote, &aSourceData };
	sMsgQueue.SendBlocking(message);
	}
	return ret;
	}

const TFrameInfo& CImageDecoderEx::FrameInfo(TInt aFrameNumber) const
{
	return iImageDecoder->FrameInfo(aFrameNumber);
}
